#include "GLES2PixelFormat.h"
#include "Root.h"
#include "RenderSystem.h"
#include "Bitwise.h"

namespace PVM
{
	GLenum GLES2PixelUtil::getGLOriginFormat(PixelFormat mFormat)
	{
		switch (mFormat)
		{
			case PF_A8:
				return GL_ALPHA;
				
			case PF_L8:
			case PF_L16:
			case PF_FLOAT16_R:
			case PF_FLOAT32_R:
				return GL_LUMINANCE;
				
			case PF_BYTE_LA:
			case PF_SHORT_GR:
			case PF_FLOAT16_GR:
			case PF_FLOAT32_GR:
				return GL_LUMINANCE_ALPHA;
			case PF_PVRTC_RGB2:
				return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
			case PF_PVRTC_RGB4:
				return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
			case PF_PVRTC_RGBA2:
				return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
			case PF_PVRTC_RGBA4:
				return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
			case PF_R5G6B5:
			case PF_B5G6R5:
			case PF_R8G8B8:
			case PF_B8G8R8:
				return GL_RGB;
			case PF_A1R5G5B5:
				return GL_BGRA;
			case PF_A4R4G4B4:
			case PF_X8R8G8B8:
			case PF_A8R8G8B8:
			case PF_B8G8R8A8:
			case PF_X8B8G8R8:
			case PF_A8B8G8R8:
				return GL_RGBA;
			default:
				return 0;
		}
	}
	
	GLenum GLES2PixelUtil::getGLOriginDataType(PixelFormat mFormat)
	{
		switch (mFormat)
		{
			case PF_A8:
			case PF_L8:
			case PF_L16:
			case PF_R8G8B8:
			case PF_B8G8R8:
			case PF_BYTE_LA:
				return GL_UNSIGNED_BYTE;
			case PF_R5G6B5:
			case PF_B5G6R5:
				return GL_UNSIGNED_SHORT_5_6_5;
			case PF_A4R4G4B4:
				return GL_UNSIGNED_SHORT_4_4_4_4;
			case PF_A1R5G5B5:
				return GL_UNSIGNED_SHORT_5_5_5_1;
				
			case PF_X8B8G8R8:
			case PF_A8B8G8R8:
			case PF_X8R8G8B8:
			case PF_A8R8G8B8:
			case PF_B8G8R8A8:
			case PF_R8G8B8A8:
				return GL_UNSIGNED_BYTE;
				
			default:
				return 0;
		}
	}
	
	GLenum GLES2PixelUtil::getGLInternalFormat(PixelFormat fmt, bool hwGamma)
	{
		switch (fmt)
		{
			case PF_L8:
			case PF_L16:
				return GL_LUMINANCE;
			case PF_A8:
				return GL_ALPHA;
			case PF_BYTE_LA:
				return GL_LUMINANCE_ALPHA;
			case PF_PVRTC_RGB2:
				return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
			case PF_PVRTC_RGB4:
				return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
			case PF_PVRTC_RGBA2:
				return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
			case PF_PVRTC_RGBA4:
				return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
			
			case PF_X8B8G8R8:
			case PF_X8R8G8B8:
			case PF_A8B8G8R8:
			case PF_A8R8G8B8:
			case PF_B8G8R8A8:
			case PF_A1R5G5B5:
			case PF_A4R4G4B4:
				return GL_RGBA;
			case PF_R5G6B5:
			case PF_B5G6R5:
			case PF_R8G8B8:
			case PF_B8G8R8:
				return GL_RGB;
			case PF_A4L4:
			case PF_R3G3B2:
			case PF_A2R10G10B10:
			case PF_A2B10G10R10:
			case PF_FLOAT16_R:
			case PF_FLOAT16_RGB:
			case PF_FLOAT16_GR:
			case PF_FLOAT16_RGBA:
			case PF_FLOAT32_R:
			case PF_FLOAT32_GR:
			case PF_FLOAT32_RGB:
			case PF_FLOAT32_RGBA:
			case PF_SHORT_RGBA:
			case PF_SHORT_RGB:
			case PF_SHORT_GR:
			case PF_DXT1:
			case PF_DXT3:
			default:
				return 0;
		}
	}
	
	GLenum GLES2PixelUtil::getClosestGLInternalFormat(PixelFormat mFormat,
													  bool hwGamma)
	{
		GLenum format = getGLInternalFormat(mFormat, hwGamma);
		if (format == GL_NONE)
		{
			if (hwGamma)
			{
				return 0;
			}
			else
			{
				return GL_RGBA;
			}
		}
		else
		{
			return format;
		}
	}
	
	PixelFormat GLES2PixelUtil::getClosestFormat(GLenum fmt, GLenum dataType)
	{
		switch(fmt)
		{
			case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
				return PF_PVRTC_RGB2;
			case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
				return PF_PVRTC_RGBA2;
			case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
				return PF_PVRTC_RGB4;
			case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
				return PF_PVRTC_RGBA2;
				
			case GL_LUMINANCE:
				return PF_L8;
			case GL_ALPHA:
				return PF_A8;
			case GL_LUMINANCE_ALPHA:
				return PF_BYTE_LA;
			case GL_RGB:
				switch(dataType)
			{
				case GL_UNSIGNED_SHORT_5_6_5:
					return PF_B5G6R5;
				default:
					return PF_R8G8B8;
			};
			case GL_RGBA:
				switch (dataType)
			{
				case GL_UNSIGNED_SHORT_5_5_5_1:
					return PF_A1R5G5B5;
				case GL_UNSIGNED_SHORT_4_4_4_4:
					return PF_A4R4G4B4;
				default:
					return PF_A8R8G8B8;
			};
			case GL_BGRA:
				return PF_A8B8G8R8;
				
			default:
				return PF_A8R8G8B8;
		}
	}
	
	size_t GLES2PixelUtil::getMaxMipmaps(size_t width, size_t height, size_t depth,
										 PixelFormat format)
	{
		size_t count = 0;
		
		do
		{
			if (width > 1)
			{
				width = width / 2;
			}
			if (height > 1)
			{
				height = height / 2;
			}
			if (depth > 1)
			{
				depth = depth / 2;
			}
			count++;
		} while (!(width == 1 && height == 1 && depth == 1));
		
		return count;
	}
	
	size_t GLES2PixelUtil::optionalPO2(size_t value)
	{
		const RenderSystemCapabilities* caps = 
		Root::getSingleton().getRenderSystem()->getCapabilities();
		
		if (caps->hasCapability(RSC_NOT_POWER_OF_2_TEXTURES))
		{
			return value;
		}
		else
		{
			return Bitwise::firstPO2From((uint32)value);
		}
	}
	
	void GLES2PixelUtil::convertToGLformat(const PixelBox& src, const PixelBox& dst)
	{
		if (dst.format == PF_A4R4G4B4)
		{
			uint16* srcptr = static_cast<uint16*>(src.data)
			+(src.left + src.top * src.rowPitch + src.front * src.slicePitch);
			uint16* dstptr = static_cast<uint16*>(dst.data)
			+ (dst.left + dst.top * dst.rowPitch + dst.front * dst.slicePitch);
			const size_t srcSliceSkip = src.getSliceSkip();
			const size_t dstSliceSkip = dst.getSliceSkip();
			const size_t k = src.right - src.left;
			for (size_t z = src.front; z < src.back; z++)
			{
				for (size_t y = src.top; y < src.bottom; y++)
				{
					for (size_t x = 0; x < k; x++)
					{
						dstptr[x] = ((srcptr[x] & 0x000F) << 12) |
						((srcptr[x] & 0x00F0) << 4) |
						((srcptr[x] & 0x0F00) >> 4) |
						((srcptr[x] & 0xF000) >> 12);
					}
					srcptr += src.rowPitch;
					dstptr += dst.rowPitch;
				}
				srcptr += srcSliceSkip;
				dstptr += dstSliceSkip;
			}
		}
	}
}
